#!/usr/bin/env python

from trac.perm import IPermissionStore
from trac.config import Option
from trac.core import *
from ConfigParser import SafeConfigParser


ADMIN_PERMS = ['TRAC_ADMIN']
WRITE_PERMS = ['MILESTONE_ADMIN', 'REPORT_ADMIN', 'TICKET_ADMIN', 'WIKI_ADMIN']
READ_PERMS = ['BROWSER_VIEW', 'TICKET_VIEW', 'MILESTONE_VIEW', 'REPORT_VIEW',
              'CHANGESET_VIEW', 'FILE_VIEW', 'LOG_VIEW', 'ROADMAP_VIEW',
              'SEARCH_VIEW', 'TIMELINE_VIEW', 'WIKI_VIEW']

__all__ = ['ProjetuPermissionStore']

class ProjetuPermissionStore(Component):
    implements(IPermissionStore)

    conf_file = Option('projetu', 'authnz_file')
    project_name = Option('projetu', 'project_name')

    def _read_config(self):
        conf = SafeConfigParser()
        conf.read(self.conf_file)
        return conf

    def _get_admins(self, conf):
        if not conf.has_option('global', 'admins'):
            return []
        admins = conf.get('global', 'admins').split(',')
        return [a.strip() for a in admins]

    def _get_project(self, conf):
        project_section = 'project:%s' % self.project_name

        # No section for this project: no access
        if not conf.has_section(project_section):
            return False, [], []

        if conf.has_option(project_section, 'public'):
            try:
                public = conf.getboolean(project_section, 'public')
            except ValueError:
                public = False
        else:
            public = False

        if conf.has_option(project_section, 'read'):
            try:
                read = conf.get(project_section, 'read').split(',')
                read = [u.strip() for u in read]
            except ValueError:
                read = []
        else:
            read = []

        if conf.has_option(project_section, 'write'):
            try:
                write = conf.get(project_section, 'write').split(',')
                write = [u.strip() for u in read]
            except ValueError:
                write = []
        else:
            write = []

        return public, read, write

    def _perm_dict(self, perms):
        return dict([(perm, True) for perm in perms])

    def _perm_tuples(self, user, perms):
        return [(user, perm) for perm in perms]

    def get_user_permissions(self, username):
        conf = self._read_config()
        admins = self._get_admins(conf)
        public, read, write = self._get_project(conf)

        # Anonymous user, grant read-only perms if the project is
        # public
        if (not username or username == 'anonymous') and public:
            return self._perm_dict(READ_PERMS)
        # Admins always get full access
        elif username in admins:
            return self._perm_dict(READ_PERMS + WRITE_PERMS + ADMIN_PERMS)
        # Writers get all write access, but not TRAC_ADMIN (so they
        # can't add other users in the webadmin component)
        elif username in write:
            return self._perm_dict(READ_PERMS + WRITE_PERMS)
        # Readers get all view permissions.
        elif username in read:
            return self._perm_dict(READ_PERMS)
        # Nope.
        else:
            return {}

    def get_all_permissions(self):
        conf = self._read_config()
        admins = self._get_admins(conf)
        public, read, write = self._get_project(conf)

        output = []

        if public:
            output += self._perm_tuples('anonymous', READ_PERMS)
        for user in admins:
            output += self._perm_tuples(
                user, READ_PERMS + WRITE_PERMS + ADMIN_PERMS)
        for user in write:
            output += self._perm_tuples(user, READ_PERMS + WRITE_PERMS)
        for user in read:
            output += self._perm_tuples(user, READ_PERMS)

        return output

    def grant_permission(self, username, action):
        """Permission granting is done via the Projetu interface. This
        therefore does nothing."""
        pass

    def revoke_permission(self, username, action):
        """Permission revoking is done via the Projetu interface. This
        therefore does nothing."""
        pass
